
﻿using System.Net.Http;
using PaymillWrapper.Models;
using PaymillWrapper.Utils;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
namespace PaymillWrapper.Service
{

    public class TransactionService : AbstractService<Transaction>
    {
        public TransactionService(HttpClient client, string apiUrl)
            : base(Resource.Transactions, client, apiUrl)
        {
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with token for the given amount in the given currency.
        /// </summary>
        /// <param name="token">Token generated by PAYMILL Bridge, which represents a credit card or direct debit.</param>
        /// <param name="amount"> Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithTokenAsync(String token, int amount, String currency)
        {
            return await CreateWithTokenAndFeeAsync(token, amount, currency, null, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with token for the given amount in the given currency.
        /// </summary>
        /// <param name="token">Token generated by PAYMILL Bridge, which represents a credit card or direct debit.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency"> ISO 4217 formatted currency code.</param>
        /// <param name="description">A short description for the transaction.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithTokenAsync(String token, int amount, String currency, String description)
        {
            return await CreateWithTokenAndFeeAsync(token, amount, currency, description, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with token for the given amount in the given currency.
        /// </summary>
        /// <param name="token">Token generated by PAYMILL Bridge, which represents a credit card or direct debit.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="fee">A Fee</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithTokenAndFeeAsync(String token, int amount, String currency, Fee fee)
        {
            return await CreateWithTokenAndFeeAsync(token, amount, currency, null, fee);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with token for the given amount in the given currency.
        /// </summary>
        /// <param name="token">Token generated by PAYMILL Bridge, which represents a credit card or direct debit.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="description">The description.</param>
        /// <param name="fee">A Fee</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithTokenAndFeeAsync(String token, int amount,
                                                                  String currency, String description, Fee fee)
        {
            ValidationUtils.ValidatesToken(token);
            ValidationUtils.ValidatesAmount(amount);
            ValidationUtils.ValidatesCurrency(currency);
            ValidationUtils.ValidatesFee(fee);

            String srcValue = String.Format("{0}-{1}", PaymillContext.GetProjectName(), PaymillContext.GetProjectVersion());

            return await createAsync(null, new UrlEncoder().EncodeObject(new
            {
                Token = token,
                Amount = amount,
                Currency = currency,
                source = srcValue,
                description = description,
                Fee_Amount = fee != null ? fee.Amount : null,
                Fee_Payment = fee != null ? fee.Payment : null
            }));
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="payment">A PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAsync(Payment payment, int amount, String currency)
        {
            return await CreateWithPaymentAsync(payment, amount, currency, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="paymentId">A PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAsync(String paymentId, int amount, String currency)
        {
            return await CreateWithPaymentAsync(new Payment(paymentId), amount, currency, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="paymentId">A PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="description">The description.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAsync(Payment payment, int amount, String currency, String description)
        {
            ValidationUtils.ValidatesPayment(payment);
            ValidationUtils.ValidatesAmount(amount);
            ValidationUtils.ValidatesCurrency(currency);
            String srcValue = String.Format("{0}-{1}", PaymillContext.GetProjectName(), PaymillContext.GetProjectVersion());
            return await createAsync(null, new UrlEncoder().EncodeObject(new
            {
                Payment = payment.Id,
                Amount = amount,
                Currency = currency,
                source = srcValue,
                description = description
            }));
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="paymentId">The Id of a PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="description">The description.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAsync(String paymentId, int amount, String currency, String description)
        {
            return await CreateWithPaymentAsync(new Payment(paymentId), amount, currency, description);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="payment">A PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="client">The PAYMILL Client which have to be charged.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAndClientAsync(Payment payment, Client client, int amount, String currency)
        {
            return await CreateWithPaymentAndClientAsync(payment, client, amount, currency, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="paymentId">The Id of a PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="clientId">The Id of a PAYMILL Client which have to be charged.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAndClientAsync(String paymentId, String clientId, int amount, String currency)
        {
            return await CreateWithPaymentAndClientAsync(paymentId, clientId, amount, currency, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="payment">A PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="client">The PAYMILL Client which have to be charged.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="description">The description.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAndClientAsync(Payment payment, Client client, int amount, String currency, String description)
        {
            ValidationUtils.ValidatesPayment(payment);
            ValidationUtils.ValidatesClient(client);
            ValidationUtils.ValidatesAmount(amount);
            ValidationUtils.ValidatesCurrency(currency);

            String srcValue = String.Format("{0}-{1}", PaymillContext.GetProjectName(), PaymillContext.GetProjectVersion());

            return await createAsync(null, new UrlEncoder().EncodeObject(new
            {
                Payment = payment.Id,
                client = client.Id,
                Amount = amount,
                Currency = currency,
                source = srcValue,
                description = description
            }));
        }

        /// <summary>
        /// CExecutes a <see cref="Transaction" /> with <see cref="Payment" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="paymentId">The Id of a PAYMILL Payment representing credit card or direct debit.</param>
        /// <param name="clientId">The Id of a PAYMILL Client which have to be charged.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="description">The description.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPaymentAndClientAsync(String paymentId, String clientId, int amount, String currency, String description)
        {
            return await CreateWithPaymentAndClientAsync(new Payment(paymentId), new Client(clientId), amount, currency);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Preauthorization" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="preauthorization">A Preauthorization, which has reserved some money from the client’s credit card.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPreauthorizationAsync(Preauthorization preauthorization, int amount, String currency)
        {
            return await CreateWithPreauthorizationAsync(preauthorization.Id, amount, currency, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Preauthorization" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="preauthorizationId">The Id of a Preauthorization, which has reserved some money from the client’s credit card.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPreauthorizationAsync(String preauthorizationId, int amount, String currency)
        {
            return await CreateWithPreauthorizationAsync(preauthorizationId, amount, currency, null);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Preauthorization" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="preauthorization">A Preauthorization, which has reserved some money from the client’s credit card.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="description">The description.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPreauthorizationAsync(Preauthorization preauthorization, int amount, String currency, String description)
        {
            return await CreateWithPreauthorizationAsync(preauthorization.Id, amount, currency, description);
        }

        /// <summary>
        /// Executes a <see cref="Transaction" /> with <see cref="Preauthorization" /> for the given amount in the given currency.
        /// </summary>
        /// <param name="preauthorizationId">The Id of a Preauthorization, which has reserved some money from the client’s credit card.</param>
        /// <param name="amount">Amount (in cents) which will be charged.</param>
        /// <param name="currency">ISO 4217 formatted currency code.</param>
        /// <param name="description">The description.</param>
        /// <returns>Transaction object indicating whether a the call was successful or not.</returns>
        public async Task<Transaction> CreateWithPreauthorizationAsync(String preauthorizationId,
            int amount, String currency, String description)
        {
            ValidationUtils.ValidatesId(preauthorizationId);
            ValidationUtils.ValidatesAmount(amount);
            ValidationUtils.ValidatesCurrency(currency);

            String srcValue = String.Format("{0}-{1}", PaymillContext.GetProjectName(), PaymillContext.GetProjectVersion());

            return await createAsync(null, new UrlEncoder().EncodeObject(new
            {
                preauthorization = preauthorizationId,
                Amount = amount,
                Currency = currency,
                source = srcValue,
                description = description
            }));
        }

        /// This function returns a <see cref="PaymillList"/>of PAYMILL Client objects. In which order this list is returned depends on the
        /// </summary>
        /// <param name="filter">Filter or null</param>
        /// <param name="order">Order or null.</param>
        /// <returns>PaymillList which contains a List of PAYMILL Client object and their total count.</returns>
        public async Task<PaymillWrapper.Models.PaymillList<Transaction>> ListAsync(Transaction.Filter filter, Transaction.Order order)
        {
            return await base.listAsync(filter, order, null, null);
        }

        /// <summary>
        /// This function returns a <see cref="PaymillList"/> of PAYMILL objects. In which order this list is returned depends on the
        /// optional parameters. If null is given, no filter or order will be applied, overriding the default count and
        /// offset.
        /// </summary>
        /// <param name="filter">Filter or null</param>
        /// <param name="order">Order or null.</param>
        /// <param name="count">Max count of returned objects in the PaymillList</param>
        /// <param name="offset">The offset to start from.</param>
        /// <returns>PaymillList which contains a List of PAYMILL objects and their total count.</returns>
        public async Task<PaymillWrapper.Models.PaymillList<Transaction>> ListAsync(Transaction.Filter filter, Transaction.Order order, int? count, int? offset)
        {
            return await base.listAsync(filter, order, count, offset);
        }

        protected override string GetResourceId(Transaction obj)
        {
            return obj.Id;
        }
    }
}